/*******************************************************************************
 * Copyright (c) 2007 University of Oslo.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     University of Oslo - initial API and implementation
 *******************************************************************************/

package com.cea.papyrus.diagram.sequence.sedi.edit;

import java.util.Iterator;
import java.util.List;
import java.util.SortedSet;

import org.eclipse.draw2d.ConnectionAnchor;
import org.eclipse.draw2d.IFigure;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.gef.ConnectionEditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.Request;
import org.eclipse.gef.RequestConstants;
import org.eclipse.gef.requests.DirectEditRequest;
import org.eclipse.gef.tools.DirectEditManager;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.uml2.uml.InteractionFragment;
import org.eclipse.uml2.uml.Lifeline;
import org.eclipse.uml2.uml.Message;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.UMLPackage;

import com.cea.papyrus.core.editor.ModelManager;
import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.diagram.sequence.sedi.edit.analysis.CoverableAnalyser;
import com.cea.papyrus.diagram.sequence.sedi.edit.analysis.CoverableAnalyser.SetEntry;
import com.cea.papyrus.diagram.sequence.sedi.edit.manager.LifelineCellEditorLocator;
import com.cea.papyrus.diagram.sequence.sedi.edit.manager.LifelineEditManager;
import com.cea.papyrus.diagram.sequence.sedi.edit.policy.DeletionEditPolicy;
import com.cea.papyrus.diagram.sequence.sedi.edit.policy.GraphConnectionPolicy;
import com.cea.papyrus.diagram.sequence.sedi.edit.policy.LifelineEditPolicy;
import com.cea.papyrus.diagram.sequence.sedi.edit.policy.LifelineLabelFormat;
import com.cea.papyrus.diagram.sequence.sedi.figures.AnchoringFigure;
import com.cea.papyrus.diagram.sequence.sedi.figures.LifelineFigure;
import com.cea.papyrus.diagram.sequence.sedi.model.command.LifelineMessageEndPosition;
import com.cea.papyrus.diagram.sequence.sedi.model.command.MessageEndPosition;
import com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil;
import com.cea.papyrus.diagraminterchange2.di2.GraphEdge;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;

/**
 * Edit part for Lifelines.
 * 
 * @author Andreas Limyr
 * @author frankd@ifi.uio.no
 */
public class LifelineEditPart extends AnchoringEditPart<Lifeline> {

    /**
     * If true, the Lifeline label is currently edited. This flag exists because the edit
     * label is different from the presentation label.
     */
    private boolean isEditModeActive;

    public LifelineEditPart(GraphElement view, ModelManager modelManager) {
        super(view, modelManager);
    }

    protected IFigure createFigure() {
        AnchoringFigure figure = new LifelineFigure();
        figure.setMessageAccess(this);
        return figure;
    }

    @Override
    protected void createEditPolicies() {
        super.createEditPolicies();
        // override the default DirectEditPolicy since lifelines have two
        // direct-editable areas
        installEditPolicy(
        		EditPolicy.DIRECT_EDIT_ROLE, 
        		new LifelineEditPolicy());
//        // installEditPolicy(EditPolicy.PRIMARY_DRAG_ROLE, new CoverableEditPolicy());
//        installEditPolicy(
//        		EditPolicy.GRAPHICAL_NODE_ROLE, 
//        		new SeDiNodeEditPolicy());
       
        // Add edit policy to handle deletion requests (replace SeDiSubpartEditPolicy)
		installEditPolicy(
				EditPolicy.COMPONENT_ROLE,
		        new DeletionEditPolicy());
		// Manage connection between this element and others
		this.installEditPolicy(
				EditPolicy.GRAPHICAL_NODE_ROLE, 
				new GraphConnectionPolicy() );
    }

    @SuppressWarnings("unchecked")
    @Override
    public Object getAdapter(Class key) {
        // if (key == IPropertySource.class)
        // return new PropertySource(getUMLModel(), getItemProvider());
        // else
        return super.getAdapter(key);
    }

    // protected IItemPropertySource getItemProvider() {
    // DefaultEditDomain domain = (DefaultEditDomain)
    // getRoot().getViewer().getEditDomain();
    // SequenceDiagramEditor editor = (SequenceDiagramEditor) domain.getEditorPart();
    // return new LifelineItemProvider(editor.getAdapterFactory());
    // }

    @Override
    @SuppressWarnings("unchecked")
    protected void refreshVisualsLabel() {
        final Lifeline lifeline = getUmlElement();
        final LifelineFigure lifelineFigure = (LifelineFigure) getFigure();

        lifelineFigure.setLabel(LifelineLabelFormat.getLabel(lifeline, isEditModeActive));
        lifelineFigure.setRefText(LifelineLabelFormat.getDecomposedAsLabel(lifeline));
        lifelineFigure.setStereotypes(LifelineLabelFormat.getStereotypeLabel(lifeline));
    }

    // TODO Consider: Replace with a child EP of the lifeline
    // that refers to the part decomposition; then delete the
    // method below

    @Override
    public void performRequest(Request request) {
        if (request.getType() == RequestConstants.REQ_OPEN)
            performOpenRequest();
        else
            super.performRequest(request);
    }

    protected void performOpenRequest() {
        // TODO Open editor with the diagram for the referred-to interaction
    }

    @Override
    protected void performDirectEdit(final Request request) {
        isEditModeActive = true;

        final LifelineFigure figure = (LifelineFigure) getFigure();
        refreshVisualsLabel();
        // figure.setLabel(LifelineLabelFormat.getLabel(getUMLModel(), isEditModeActive));
        figure.getUpdateManager().performUpdate(); // TODO Can this line be removed?

        final int target;
        if (request instanceof DirectEditRequest) {
            DirectEditRequest deRequest = (DirectEditRequest) request;
            target = figure.findEditTargetAt(deRequest.getLocation());
        } else
            target = figure.findEditTargetAt(null);

        final DirectEditManager manager =
                new LifelineEditManager(this, TextCellEditor.class, new LifelineCellEditorLocator(
                        figure, target), target);
        manager.show();
    }

    public void finishEditing() {
        isEditModeActive = false;
        refreshVisualsLabel();
    }

    @Override
    protected void umlModelChanged(Notification evt) {
        switch (evt.getFeatureID(UMLPackage.class)) {
        case UMLPackage.LIFELINE__REPRESENTS:
        case UMLPackage.LIFELINE__DECOMPOSED_AS:
        case UMLPackage.LIFELINE__SELECTOR:
        case UMLPackage.PROPERTY__TYPE:
        case UMLPackage.PART_DECOMPOSITION__REFERS_TO:
//            // == case OPAQUE_EXPRESSION__BODY:
//            if (evt.getOldValue() instanceof EObject)
//                deactivate((EObject) evt.getOldValue());
//            if (evt.getNewValue() instanceof EObject)
//                activate((EObject) evt.getNewValue());
//            // fallthrough

        case UMLPackage.ELEMENT__EANNOTATIONS: // catches some (but not all) applied
            // stereotypes
        case UMLPackage.NAMED_ELEMENT__NAME:
            // == case INTERACTION__NAME, CLASS__NAME, TYPE__NAME etc.
            refreshVisuals();
            break;
        }
        super.umlModelChanged(evt);
    }

    /**
     * Finds the position of the given anchor on the lifeline represented by this
     * editpart.
     * 
     * @param anchor
     *            The anchor whose position on the lifeline is desired.
     * @param send
     *            set to <code>true</code> if the anchor is to be considered as the send
     *            end of a message, or <code>false</code> if the anchor should be
     *            considered the receive end of a message.
     * @return an object that specifies the exact model position of a message sent or
     *         received at the anchor. TODO Refactor - see comment in superclass
     */
    @Override
    @SuppressWarnings("unchecked")
    public MessageEndPosition getMessagePosition(final ConnectionAnchor anchor, boolean send,
            final NamedElement owner) {
        final GraphNode lifelineView = (GraphNode) getModel();
        final Lifeline lifeline = ModelUtil.<Lifeline> getSemantic(lifelineView);

        final LifelineMessageEndPosition position = new LifelineMessageEndPosition();
        position.setLifeline(lifeline);
        position.setOwner(owner);

        final CoverableAnalyser analyser =
                new CoverableAnalyser(this, ModelUtil.getGraphNode(lifeline.getInteraction(),
                        LookForElement.getDiagram(lifelineView)));
        final SortedSet<CoverableAnalyser.SetEntry> orderedFragments =
                analyser.collectFragments(lifelineView);

        // final Point anchorLocalLoc = anchor.getLocation(null);
        // getFigure().translateToRelative(anchorLocalLoc);

        final SetEntry testEntry = new SetEntry(anchor.getLocation(null).y, send, null);
        orderedFragments.add(testEntry);

        InteractionFragment previous = null, preprevious = null;
        for (final SetEntry setEntry : orderedFragments) {
            if (setEntry == testEntry) {
                // extra condition: when moving a message downwards, the
                // "previous" eventoccurrence will be the old eventoccurrence
                // (since it has not been moved yet). In this case we have to
                // skip an addition eventoccurrence.
                final Message message = getMessage(anchor, send);
                if (message != null) {
                    final boolean skipOneLess =
                            (previous == (send ? message.getSendEvent() : message.getReceiveEvent()));
                    if (skipOneLess) {
                        position.setLifelinePosition(preprevious);
                        return position;
                    }
                }
                position.setLifelinePosition(previous);
                return position;
            }
            preprevious = previous;
            previous = setEntry.getFragment();
        }
        throw new AssertionError("testEntry not in set");
    }

    /**
     * Returns the Message that ends in the given anchor.
     */
    @SuppressWarnings("unchecked")
    private Message getMessage(final ConnectionAnchor anchor, final boolean source) {
        if (source) {
            for (ConnectionEditPart a : (List<ConnectionEditPart>) getSourceConnections()) {
                if (getSourceConnectionAnchor(a) == anchor)
                    return ModelUtil.<Message> getSemantic((GraphEdge) a.getModel());
            }
        } else {
            for (ConnectionEditPart a : (List<ConnectionEditPart>) getTargetConnections()) {
                if (getTargetConnectionAnchor(a) == anchor)
                    return ModelUtil.<Message> getSemantic((GraphEdge) a.getModel());
            }
        }
        return null;
    }

    // --- Activation ---
    @Override
    public void activate() {
		if (this.isActive()) {
			return;
		}
		super.activate();
		// start listening for changes in the model
		((Notifier)this.getModel()).eAdapters().add(graphElementListener);
		Iterator<com.cea.papyrus.diagraminterchange2.di2.Property> propertiesIterator= 
			((GraphNode) this.getModel()).getProperty().iterator();
		while(propertiesIterator.hasNext()){
			((Notifier)propertiesIterator.next()).eAdapters().add(graphElementListener);
		}
    }

    @Override
    public void deactivate() {
		if (!this.isActive()) {
			return;
		}

		// stop listening for changes in the model
		((Notifier)this.getModel()).eAdapters().remove(graphElementListener);
		Iterator<com.cea.papyrus.diagraminterchange2.di2.Property> propertiesIterator= 
			((GraphNode) this.getModel()).getProperty().iterator();
		while(propertiesIterator.hasNext()){
			((Notifier) propertiesIterator.next()).eAdapters().remove(graphElementListener);
		}		
		
		super.deactivate();
    }
    
//    // --- Activation ---
//
//    @Override
//    public void activate() {
//        super.activate();
//        activate(getUmlElement().getDecomposedAs());
//        activate(getUmlElement().getRepresents());
//        activate(getUmlElement().getSelector());
//    }
//
//    @SuppressWarnings("unchecked")
//    public void activate(EObject obj) {
//        if (obj instanceof PartDecomposition) {
//            obj.eAdapters().add(umlAdapter);
//            activate(((PartDecomposition) obj).getRefersTo());
//        } else if (obj instanceof ConnectableElement) { // Lifeline.Represents
//            obj.eAdapters().add(umlAdapter);
//            if (obj instanceof Property)
//                activate(((Property) obj).getDatatype());
//        } else if (obj instanceof OpaqueExpression) // Lifeline.Selector
//            obj.eAdapters().add(umlAdapter);
//        else if (obj instanceof Interaction) // Lifeline.DecomposedAs.RefersTo
//            obj.eAdapters().add(umlAdapter);
//        else if (obj instanceof Type) // Lifeline.Represents.Type
//            obj.eAdapters().add(umlAdapter);
//    }
//
//    @Override
//    public void deactivate() {
//        deactivate(getUmlElement().getRepresents());
//        deactivate(getUmlElement().getDecomposedAs());
//        deactivate(getUmlElement().getSelector());
//        super.deactivate();
//    }
//
//    private void deactivate(EObject obj) {
//        if (obj instanceof PartDecomposition) {
//            deactivate(((PartDecomposition) obj).getRefersTo());
//            obj.eAdapters().remove(umlAdapter);
//        } else if (obj instanceof ConnectableElement) { // Lifeline.Represents
//            if (obj instanceof Property)
//                deactivate(((Property) obj).getDatatype());
//            obj.eAdapters().remove(umlAdapter);
//        } else if (obj instanceof OpaqueExpression) // Lifeline.Selector
//            obj.eAdapters().remove(umlAdapter);
//        else if (obj instanceof Interaction)
//            obj.eAdapters().remove(umlAdapter);
//        else if (obj instanceof DataType) // Lifeline.Represents.DataType
//            obj.eAdapters().remove(umlAdapter);
//    }
}
